Django Admin 速记
作为Python平台上的约定类框架,Django Admin因为可以非常方便地管理数据表而经常被我们使用。尽管功能强大,但动辄好几个月的使用间隔会让重新上手时不知所措。这里针对常用case进行速记。
创建项目
安装django-admin命令行工具,创建项目
1 | django-admin startproject <project_name> |
多应用
一个项目可以管理多个数据源,每个数据源作为一个应用,步骤如下
创建应用
1
python manage.py startapp <app_name>
定义数据库路由器,参考手册:https://docs.djangoproject.com/en/4.1/topics/db/multi-db/#an-example
修改settings.py,主要是 DATABASES 和 DATABASE_ROUTERS、INSTALLED_APPS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'xxx',
'USER': 'xxx',
'PASSWORD': 'xxx',
'HOST': 'xxx',
'PORT': 5432,
},
# 添加app的数据源
'<app_name>': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'xxx',
'USER': 'xxx',
'PASSWORD': 'xxx',
'HOST': 'xxx',
'PORT': 5432,
},
}
# 指定数据库路由器
DATABASE_ROUTERS = ['<上一步定义的router>']
# 安装新增的app
INSTALLED_APPS.append("<app_name>")
默认数据源
Django Admin的用户、鉴权相关表格都放在default数据源下。即DATABASES中key为default的数据源
Model
生成Model
存在多个app时,可以只从单个app的数据源生成Model
1 | python manage.py inspectdb --database <app_name> |
Model迁移
迁移指根据model.py中定义的模型对数据库进行修改。一般需要有两个步骤
- makemigrate,生成数据库迁移python脚本
- migrate,执行迁移
这意味着数据库的表结构会随着Django Admin项目的model定义变化。在我们的使用场景下这显然是不合理的:每个app有自己的数据表管理方式,Django Admin项目仅仅作为表数据查看和修改的窗口。
为了解决这个问题,在migrate时加上 –run-syncdb 参数:对库中已存在的表,migrate不会做创建和修改操作。
可以不migrate吗
不可以。至少在model刚定义时候需要migrate一次
migrate除了应用model的修改,还会将新增的model表增加到权限表。如果不migrate,在用户权限管理则不会存在对应model的权限。这意味着只有管理员才能看到该应用。
Model元数据
Model类允许定义内部类,指明一些元数据,比如
1 | class User(models.Model): |
- managed:是否受Django Admin的migrate管理。设置为否时,migrate会忽略对该model的所有操作
- db_table:Model对应的数据表名
- verbose_name:Model在页面显示的单数名称。默认的复数名称会在后面直接加s
- verbose_name_plural:Model在页面显示的复数名称
自定义字段
在Model类中定义方法,在显示时引用,能够显示该方法的返回值,比如下面这个Model定义
1 | class Resource(models.Model): |
ModelAdmin中使用时如下
1 |
|
表关联 - 外键
外键关联在Django Admin中有很多好处,典型的可以做内联。即如果A表记录是B表记录的外键,则B表的详情页中,可以直接编辑或查看A表记录,而不必再单独到A表搜索。一个外键定义如下
1 | class Subscription(models.Model): |
- 外键不一定需要真的在数据库中存在。我们可以将具有一对多关联的两个表声明称外键关系,以享受其带来的便利。
- 外键字段默认为 xxx_id,外键对应的字段为xxx.id,比如user表的id字段是blog表的外键,外键字段名为blog.user_id,且要求user.id必须声明为主键
- 上述规则中外键字段名可以修改,上述例子中,我们将subscryption的user_id和subscryption_history表的user_id字段作为外键关联
Model管理
基本属性
- list_display:列表页显示的列名
- list_editable:列表页可以编辑的列名
- list_filter:列表页中可以通过下拉选过滤的列。列表页顶部会出现下拉选框,下拉选的内容是对应字段做select distinct 的结果。所以需要注意到设置该字段会带来的额外查询负担
- fields:详情页显示的列名,默认显示所有
- readonly_fields:详情页的只读字段(详情页除主键外默认所有字段都是可编辑的)
- search_fields:列表页顶部出现一个搜索框,该搜索框输入的内容作为哪些字段的搜索依据。
- 设置多个field时,它们之间是or关系
- 默认是模糊匹配,写成’=<field>’为精确匹配
- 因为 Django Admin 本身的支持问题,搜索框无法自定义place holder
- date_hierachy:在列表页顶部显示日期层级面包屑,该字段指明日期数据来源
- ordering:列表显示结果按照哪些字段排序。负号表示倒序
- list_per_page:每页显示多少条数据,默认为 100
自定义显示字段
前面说了Model中自定义的方法可以作为自定义字段显示。ModelAdmin中自定义方法也可以作为自定义字段显示
1 |
|
自定义ListFilter
list_filter字段可以是自定义的,只要定义好下拉选内容来源(重写lookups方法)、查询方法即可
1 | class CommentListFilter(admin.SimpleListFilter): |
自定义action
列表页顶部会显示一个操作列表,可通过如下方式向列表中增加操作
1 | def re_count_commented_in_video(video_id: str): |
自定义html组件样式
显示字段时,每一种Model的filed类型都有固定的html元素样式对应,比如Text类型对应<textarea>元素。可以通过如下方式自定义
1 |
|
禁用删除和添加
重写如下方法能够禁用删除和添加action
1 |
|
内联
在Model满足外键关系的情况下,可按照如下方式定义内联效果
1 | class SubscriptionHistoryInline(admin.TabularInline): |
创建API
view中定义请求处理方法
1
2
3
4
5
6
7# csrf_exempt 注解可以忽略csrftoken的校验
def encrypt_content(request):
content = request.body.decode()
cmd = os.getcwd() + "/encryption -enc -str " + content
encrypted_content = os.popen(cmd).read()
return HttpResponse(encrypted_content)做urls.py中做映射
1
2
3
4urlpatterns = [
# ... ...
path('api/encrypt', meiji_app.views.encrypt_content),
]
增加自定义页面
如果需要在某个应用下添加一个自定义入口,指向自定义页面。可以定义空Model和ModelAdmin,然后在ModeAdmin中重写changelist_view,将默认view指向自定义的view。具体如下
在目标app目录下创建 templates 目录,并添加模板html,比如下面这样
settings.py中将该templates路径添加到模板目录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
# 关键是这行
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]views.py中定义方法。这里的关键是 render_to_string 方法,其能够将模板直接转换为字符串,方便直接返回客户端
1
2def tool(request):
return HttpResponse(render_to_string("tool.html", request=request))定义空Model
1
2
3
4
5class Tool(models.Model):
class Meta:
verbose_name = '工具'
verbose_name_plural = '工具'定义ModelAdmin,将列表view换到上面自定义的view
1
2
3
4
class ToolAdmin(GeneralAdmin):
def changelist_view(self, request, extra_context=None):
return tool(request)
能够得到如下效果
容器化
给一个能用的Dockerfile
1 | FROM python:3.10 |
健康检查
可以使用 django-health-check 增加一个健康检查接口。按照其配置,健康检查接口为:localhost:8000/ht/